#pragma once

#include <atlstr.h>
class CScene;
class CCamera;

class CHeightMap;
class CTerrain;
class CPlayer;
class CCamera;

typedef struct tagTEXTUREITEMSTRUCT
{
    LPWSTR						m_pstrFileName;   
    LPDIRECT3DTEXTURE9			m_pd3dTexture;    
} TEXTUREITEMSTRUCT;

typedef struct tagATTRIBUTEITEMSTRUCT
{
    int							m_nTexture;
    int							m_nMaterial;
} ATTRIBUTEITEMSTRUCT;

typedef struct tagBOUNDINGCUBE
{
    D3DXVECTOR3					m_vMinimum;
    D3DXVECTOR3					m_vMaximum;
} BOUNDINGCUBE;

class CVertex
{
public:
    D3DXVECTOR3					m_vPosition; 

    CVertex(float x, float y, float z) { m_vPosition = D3DXVECTOR3(x, y, z); }
    CVertex(D3DXVECTOR3 vPosition) { m_vPosition = vPosition; }
    CVertex() { m_vPosition = D3DXVECTOR3(0.0f, 0.0f, 0.0f); }
};

class CDiffuseVertex
{
public:
    D3DXVECTOR3					m_vPosition;  
	DWORD						m_dwDiffuse;   

    CDiffuseVertex(float x, float y, float z, DWORD dwColor) { m_vPosition = D3DXVECTOR3(x, y, z); m_dwDiffuse = dwColor; }
    CDiffuseVertex() { m_vPosition = D3DXVECTOR3(0.0f, 0.0f, 0.0f); m_dwDiffuse = D3DCOLOR_XRGB(0, 0, 0xff); }
};

#define FVF_TEXTURE_1_SET		(D3DFVF_XYZ | D3DFVF_TEX1)

class CTexturedVertex
{
public:
    CTexturedVertex(float x, float y, float z, float u1, float v1) { m_vPosition = D3DXVECTOR3(x, y, z); m_vTexture1 = D3DXVECTOR2(u1, v1); }
    CTexturedVertex(D3DXVECTOR3 vPosition, D3DXVECTOR2 vTexture1) { m_vPosition = vPosition; m_vTexture1 = vTexture1; }
    CTexturedVertex() { m_vPosition = D3DXVECTOR3(0.0f, 0.0f, 0.0f); m_vTexture1 = D3DXVECTOR2(0.0f, 0.0f); }

    D3DXVECTOR3					m_vPosition;  
	D3DXVECTOR2					m_vTexture1;   
};

#define FVF_NORMAL_TEX2 (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX2)
class CNormalTexture2 {
public:	
	D3DXVECTOR3		m_vPosition;
	D3DXVECTOR3		m_vNormal;
	D3DXVECTOR2		m_Tex1;
	D3DXVECTOR2		m_Tex2;	
};

class CMesh
{
private:
	int							m_nReferences;
protected:
    LPD3DXMESH					m_pd3dxMesh; 
	DWORD						m_nAttributes;
    ATTRIBUTEITEMSTRUCT			*m_pAttributeItemIDs;   
public:
	CMesh();
    virtual ~CMesh();

	void AddRef();
	void Release();
	
	BOUNDINGCUBE				m_bcBoundingCube;
		
	virtual void Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions=D3DXMESH_MANAGED) { };
	bool LoadFromXFile(LPCTSTR lpstrFileName, DWORD dwOptions, LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene);
	virtual void Render(LPDIRECT3DDEVICE9 pD3DDevice);
	virtual void Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene);
	BOOL CheckRayIntersection(LPDIRECT3DDEVICE9 pD3DDevice, D3DXMATRIX *pmtxWorld, D3DXMATRIX *pmtxView, D3DXVECTOR3 *pvPickPosition, float *pfNearHitDistance);
};

class CCubeMesh : public CMesh
{
public:
	CCubeMesh();
	virtual void Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions=D3DXMESH_MANAGED);
	virtual void Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene);
};

class CSpaceShipMesh : public CMesh
{
public:
	CSpaceShipMesh();
	virtual void Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions=D3DXMESH_MANAGED);
	virtual void Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene);
};

class CAsteroidMesh : public CMesh
{
public:
	CAsteroidMesh();
	virtual void Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions=D3DXMESH_MANAGED);
	virtual void Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene);
};

class CCruiser01Mesh : public CMesh
{
public:
	CCruiser01Mesh();
	virtual void Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions=D3DXMESH_MANAGED);
	virtual void Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene);
};

class CCruiser02Mesh : public CMesh
{
public:
	CCruiser02Mesh();
	virtual void Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions=D3DXMESH_MANAGED);
	virtual void Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene);
};

class CSkyBoxMesh : public CMesh
{
public:
	CSkyBoxMesh();
	~CSkyBoxMesh();
	virtual void Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions=D3DXMESH_MANAGED);
};

class CGameObject
{
public:
	CGameObject(CMesh *pMesh);
	CGameObject();
	~CGameObject(void);
	void SetMesh(CMesh *pMesh);

	virtual void Render(LPDIRECT3DDEVICE9 pD3DDevice);
	virtual void Render(LPDIRECT3DDEVICE9 pD3DDevice, CCamera *pCamera);
	virtual void Render(LPDIRECT3DDEVICE9 pD3DDevice, CCamera *pCamera, CScene *pScene);

	void MoveForward(float fDistance=0.5f);
	void MoveUpDown(float fDistance=0.5f);
	void MoveStrafe(float fDistance=0.5f);

    float           			m_fPitch;             
    float           			m_fRoll;              
    float           			m_fYaw;               

	void Rotate(float x, float y, float z);

    D3DXMATRIX					m_mtxWorld;             
    CMesh						*m_pMesh;                
};

class CSkyBox : public CGameObject
{
public:
	CSkyBox();
	~CSkyBox();
	virtual void Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions=D3DXMESH_MANAGED);
	virtual void Render(LPDIRECT3DDEVICE9 pD3DDevice, CCamera *pCamera, CScene *pScene);
};




// about terrain

class CHeightMap
{
    BYTE						*m_pHeightMapImage;       
    int							m_nWidth;  
    int							m_nHeight; 
    D3DXVECTOR3					m_vScale;         
public:
	CHeightMap(void);
	~CHeightMap(void);

    bool LoadHeightMap(LPCTSTR pFileName, int nWidth, int nHeight);
    float GetHeight(float x, float z, bool bReverseQuad = false);
    D3DXVECTOR3 GetHeightMapNormal(int x, int z);
	D3DXVECTOR3 GetHeightMapNormal(float fx, float fz);
	BYTE *GetHeightMapImage() { return(m_pHeightMapImage); }
	D3DXVECTOR3 GetScale() { return(m_vScale); }
	int GetHeightMapWidth() { return(m_nWidth); }
	int GetHeightMapHeight() { return(m_nHeight); }
};



class CGridMesh : public CMesh
{
protected:
	int							m_xStart;
	int							m_zStart;
    unsigned short				m_nWidth;                  
    unsigned short				m_nHeight;                  
    unsigned short				m_cxQuads;      
    unsigned short				m_czQuads;     
public:
	CGridMesh();
	CGridMesh(int xStart, int zStart, int nWidth, int nHeight);
	~CGridMesh();
	//virtual void Build(LPDIRECT3DDEVICE9 pD3DDevice, DWORD dwUsage=0, D3DPOOL dwMemoryPool=D3DPOOL_MANAGED) { }
	virtual void Build(LPDIRECT3DDEVICE9 pD3DDevice, CHeightMap *pHeightMap, DWORD dwMeshCreateOptions=D3DXMESH_MANAGED);	
	void Render(LPDIRECT3DDEVICE9 pD3DDevice);
};


class CTerrain
{
public:
    CHeightMap					m_HeightMap;       
    int							m_cxTerrain;  
    int							m_czTerrain; 
	int							m_nBlockWidth;
	int							m_nBlockHeight;
	int							m_cxQuadsPerBlock;
	int							m_czQuadsPerBlock;
    CGridMesh					**m_ppMesh;            
    int							m_nMeshes;       
    D3DXVECTOR3					m_vScale;         
    D3DXMATRIX					m_mtxWorld;             

	ATTRIBUTEITEMSTRUCT			m_Attrubute[2];
	
	D3DMATERIAL9				m_Material;

	CTerrain(void);
	~CTerrain(void);

	LPDIRECT3DTEXTURE9			m_MapTex;
	LPDIRECT3DTEXTURE9			m_DetailTex;
	

	void Build(CScene *pScene, LPDIRECT3DDEVICE9 pD3DDevice, DWORD dwUsage=0, D3DPOOL dwMemoryPool=D3DPOOL_MANAGED);
	void Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene);

    static void UpdatePlayerCallback(LPVOID pContext, CPlayer *pPlayer, float fTimeScale);
    static void UpdateCameraCallback(LPVOID pContext, CCamera *pCamera, float fTimeScale);
};
